/*
 * 
 * Copyright 2011 visual-analysis-of-cancer.
 * This file is part of visual-analysis-of-cancer.
 * 
 * visual-analysis-of-cancer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * visual-analysis-of-cancer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with kOSMik.  If not, see <http://www.gnu.org/licenses/>.
 *  
 * visual-analysis-of-cancer:
 * An tool to analye the WHO and GLOBOCAN cancer data.
 * http://code.google.com/p/visual-analysis-of-cancer/
 * 
 * 
 * 
 * Authors: Erik Sniegula, Johannes Dahlke
 * 
 * Module:  Programming in Processing for Visual Analytics
 * Group:   20
 * 
 * Created: Aug. 2011
 * Version: 1.0
 *
 */
 




package de.fuberlin.processing.who.cancer.gui;

import java.util.Collection;
import java.util.Observable;
import java.util.Observer;

import processing.core.PApplet;
import processing.core.PImage;
import controlP5.ControlCanvas;
import controlP5.ControlGroup;
import controlP5.ControlP5;
import controlP5.Controller;
import controlP5.ControllerGroup;
import controlP5.ControllerInterface;
import controlP5.Tab;
import de.fuberlin.processing.who.cancer.controlp5ex.ExpandableControlGroup;
import de.fuberlin.processing.who.cancer.controlp5ex.LinkLabel;
import de.fuberlin.processing.who.cancer.data.DBException;
import de.fuberlin.processing.who.cancer.data.Database;
import de.fuberlin.processing.who.cancer.data.DatabaseConnector;
import de.fuberlin.processing.who.cancer.data.structs.StructLocationData;
import de.fuberlin.processing.who.cancer.gui.detailgraph.BarChart;
import de.fuberlin.processing.who.cancer.gui.detailgraph.CancerCauseAgeBarChart;
import de.fuberlin.processing.who.cancer.gui.detailgraph.CancerCauseAgeSpreadingByCancerIncidenceBarChart;
import de.fuberlin.processing.who.cancer.gui.detailgraph.CancerCauseAgeSpreadingByCancerMortalityBarChart;
import de.fuberlin.processing.who.cancer.gui.detailgraph.CancerCauseAirPollutionBarChart;
import de.fuberlin.processing.who.cancer.gui.detailgraph.CancerCauseUvRadiationBarChart;
import de.fuberlin.processing.who.cancer.gui.detailgraph.CancerIncidenceBarChart;
import de.fuberlin.processing.who.cancer.gui.detailgraph.CancerMortalityBarChart;
import de.fuberlin.processing.who.cancer.gui.detailgraph.HealthFinancingBarChart;
import de.fuberlin.processing.who.cancer.gui.detailgraph.LifeExpectancyBarChart;
import de.fuberlin.processing.who.cancer.gui.scatterplot.Cell;
import de.fuberlin.processing.who.cancer.gui.scatterplot.CellConstructionAndProcessingParcel;
import de.fuberlin.processing.who.cancer.gui.scatterplot.CellDataProcessor;
import de.fuberlin.processing.who.cancer.interaction.CancerCauses;
import de.fuberlin.processing.who.cancer.interaction.CausesBroadcaster;
import de.fuberlin.processing.who.cancer.interaction.LocationBroadcaster;
import de.fuberlin.processing.who.cancer.interaction.LocationModifier;
import de.fuberlin.processing.who.cancer.interaction.TreatedVariables;
import de.fuberlin.processing.who.cancer.run.CancerSketch;
import de.fuberlin.processing.who.cancer.utils.Fonts;
import de.fuberlin.processing.who.cancer.utils.Notification;
import de.fuberlin.processing.who.cancer.utils.Test;


/**
 * 
 * @author erik
 * 
 */

public class DetailGraph extends Controller implements Observer,
		CellDataProcessor {

	public static final int NUM_GRAPH_WIDTH = CancerSketch.NUM_SKETCH_WIDTH
			- HealthGraph.NUM_GRAPH_WIDTH - 3 * CancerSketch.NUM_SKETCH_PADDING;
	public static final int NUM_GRAPH_HEIGHT = CancerSketch.NUM_SKETCH_HEIGHT
			- Scatterplot.NUM_PLOT_HEIGHT - 2 * CancerSketch.NUM_SKETCH_PADDING
			- ParameterMenu.NUM_LIST_HEIGHT;
	private static final String URL_GLOBOCAN_MORE_INFORMATION = "http://globocan.iarc.fr/factsheets/populations/factsheet.asp?uno=%d";

	private final int rowHeight = 20;
	private int left;
	private int top;
	private LocationModifier locationModifier;
	private DatabaseConnector db;
	private ExpandableControlGroup controlGroup;
	private BarChart leftChart;
	private BarChart rightChart;
	private Collection<Integer> selectedLocations = null;
	private PApplet theApplet = ControlP5.papplet;
	private LinkLabel[] linkLabels = new LinkLabel[0];
	private int margin = 5;
	private Controller thisControl;
	private int canvasColor;


	public DetailGraph( ControlP5 theControlP5, String theName, int theX, int theY) {
		super( theControlP5, (Tab) ( theControlP5.getTab( "default")), theName,
				theX, theY, NUM_GRAPH_WIDTH, NUM_GRAPH_HEIGHT);
		this.locationModifier = LocationBroadcaster.get();
		this.locationModifier.addObserver( this);
		this.left = theX;
		this.top = theY;
		thisControl = this;
		canvasColor = this.getColor().getBackground();
		this.setColorForeground( 255);

		initControlGroup();

		initBarCharts();
		connectToDatabase();
		this.getCaptionLabel().setVisible( false);

		update();
	}


	private void initControlGroup() {
		controlGroup = new ExpandableControlGroup( controlP5,
				(ControllerGroup) controlP5.controlWindow.tabs().get( 1),
				"detailGraphControlGroup", left, top, width, height, height) {

			@Override
			public void onExpand() {
				thisControl.update();
			}


			@Override
			public void onShrink() {
				thisControl.update();
			}
		};
		controlGroup.hideBar();
		controlGroup.setColorBackground( canvasColor);
		controlGroup.setColorForeground( this.getColor().getForeground());

		controlGroup.setHeight( this.getHeight());
		controlGroup.addCanvas( new ControlCanvas() {

			@Override
			public void draw( PApplet p) {
			}
		});
		controlP5.register( controlGroup);
	}


	private void initBarCharts() {
		setChartsDependOnTreatedVariables( TreatedVariables.HEALTH_FINANCING,
				TreatedVariables.LIFE_EXPECTANCY);
	}


	private BarChart getNewChartDependOnTreatedVariable( int left, int top,
			int width, int height, TreatedVariables... variables) {
		switch ( variables[0]) {
			case LIFE_EXPECTANCY:
				return new LifeExpectancyBarChart( controlP5, controlGroup,
						"detailBarChart_" + variables[0].toString(), left, top, width,
						height);
			case HEALTH_FINANCING:
				return new HealthFinancingBarChart( controlP5, controlGroup,
						"detailBarChart_" + variables[0].toString(), left, top, width,
						height);
			case CANCER_INCIDENCE:
				return new CancerIncidenceBarChart( controlP5, controlGroup,
						"detailBarChart_" + variables[0].toString(), left, top, width,
						height);
			case CANCER_MORTALITY:
				return new CancerMortalityBarChart( controlP5, controlGroup,
						"detailBarChart_" + variables[0].toString(), left, top, width,
						height);
			case CANCER_CAUSE_AIR_POLLUTION:
				return new CancerCauseAirPollutionBarChart( controlP5, controlGroup,
						"detailBarChart_" + variables[0].toString(), left, top, width,
						height);
			case CANCER_CAUSE_UV_RADIATION:
				return new CancerCauseUvRadiationBarChart( controlP5, controlGroup,
						"detailBarChart_" + variables[0].toString(), left, top, width,
						height);
			case CANCER_CAUSE_AGE:
				if ( variables[1] == TreatedVariables.CANCER_INCIDENCE) {
					return new CancerCauseAgeSpreadingByCancerIncidenceBarChart(
							controlP5, controlGroup, "detailBarChart_"
									+ variables[0].toString(), left, top, width, height);
				} else {
					return new CancerCauseAgeSpreadingByCancerMortalityBarChart(
							controlP5, controlGroup, "detailBarChart_"
									+ variables[0].toString(), left, top, width, height);
				}
			case CANCER_CAUSE_SEX:
			default:
				try {
				  throw new NotImplementedException();
				} catch ( NotImplementedException e) {
					Notification.printDebugException( e);
				}
				return null;
				// return new CancerMortalityBarChart( controlP5, controlGroup,
				// "detailBarChart_" + variable.toString(), left, top, width, height);
		}
	}


	private void setChartsDependOnTreatedVariables(
			TreatedVariables independentVariable, TreatedVariables dependentVariable) {
		if ( Test.isAssigned( leftChart))
			leftChart.remove();
		if ( Test.isAssigned( rightChart))
			rightChart.remove();
		System.gc();

		leftChart = getNewChartDependOnTreatedVariable( margin, margin, 125, 0,
				independentVariable, dependentVariable);
		leftChart.setLeftOriented();

		rightChart = getNewChartDependOnTreatedVariable( width - 125 - margin,
				margin, 125, 0, dependentVariable, independentVariable);
		rightChart.setRightOriented();

		controlGroup.update();
		this.update();
	}


	private void clearLinkLabels() {
		if ( Test.isAssigned( linkLabels)) {
			for ( int i = linkLabels.length - 1; i >= 0; i--) {
				if ( Test.isAssigned( linkLabels[i])) {
					linkLabels[i].unregisterTooltip();
					linkLabels[i].remove();
					linkLabels[i] = null;
				}
			}
			System.gc();
		}
	}


	private void updateLinkLabels() {
		clearLinkLabels();
		controlGroup.update();
		linkLabels = new LinkLabel[selectedLocations.size()];
		StructLocationData locationData;
		String locationUrl;
		int i = 0;
		for ( Integer locationId : selectedLocations) {
			locationData = db.getLocationDataByLocationId( locationId);
			locationUrl = String.format( URL_GLOBOCAN_MORE_INFORMATION,
					locationData.populationId);
			if ( !( margin + ( rowHeight * i) + rowHeight > controlGroup
					.getVisibleBoxHeight() - 20)) {
				linkLabels[i] = new LinkLabel( controlP5, controlGroup,
						"detailGraphLinkLabel" + i, locationData.locationName, locationUrl,
						margin, margin + ( rowHeight * i), controlGroup.getWidth() - 2
								* margin, rowHeight, theApplet.color( 111),
						Fonts.getDejavuSans(), 11, true);
				linkLabels[i].registerTooltip( String.format(
						"Click for more info. of %s (GLOBOCAN/Web-Browser).", locationData.locationName));
				linkLabels[i].setLabelColor( canvasColor);
				i++;
			} else {
				break;
			}

		}
	}


	private void connectToDatabase() {
		try {
			db = Database.get();
		} catch ( DBException e) {
			e.printStackTrace();
		}
	}


	// overwrite the draw method for the controller's visual representation.
	public void draw( PApplet p) {
	}


	@Override
	public void update() {
		if ( Test.isAssigned( selectedLocations = locationModifier.getLocations())
				&& Test.isCollectionContainingElements( selectedLocations)) {
			int expandedControlBoxHeight = selectedLocations.size() * rowHeight + 2
					* margin + 40;
			controlGroup.setExpandedHeight( expandedControlBoxHeight);
			int chartHeight = Math.min( leftChart.neededHeight(),
					controlGroup.getVisibleBoxHeight() - 2 * margin);
			leftChart.setHeight( chartHeight);
			rightChart.setHeight( chartHeight);
			updateLinkLabels();
		} else {
			controlGroup.setExpandedHeight( 0);
			linkLabels = null;
		}
		super.update();
	}


	@Override
	public Controller setValue( float theValue) {
		// TODO Auto-generated method stub
		return null;
	}


	@Override
	public void update( Observable observable, Object arg) {
		if ( observable instanceof LocationBroadcaster) {
			locationModifier.getLocations();
			Collection<Integer> selectedLocations = locationModifier.getLocations();
			updateLocations( selectedLocations);
		}
	}


	private void updateLocations( final Collection<Integer> selectedLocations) {
		update();
	}


	@Override
	public Controller setHeight( int theHeight) {
		update();
		return super.setHeight( theHeight);
	}


	@Override
	public void processDataFromCell( Cell theCell) {
		CellConstructionAndProcessingParcel parcel = theCell
				.getCellConstructionAndProcessingParcel();
		setChartsDependOnTreatedVariables( parcel.independentVariable,
				parcel.dependentVariable);
	}

}
